//
//  ViewController.m
//  ololo
//
//  Created by Администратор on 9/14/12.
//  Copyright (c) 2012 Администратор. All rights reserved.
//

#import "ViewController.h"


#define QUADS_COUNT 10000
#define VERTS_PER_QUAD 4


enum eAttributes
{
	ATTRIB_POSITION,
    ATTRIB_COLOR,
    NUM_ATTRIBUTES
};

GLint attributes[NUM_ATTRIBUTES];


static const GLfloat identityMatrix[] =
{
	1, 0, 0, 0,
	0, 1, 0, 0,
	0, 0, 1, 0,
	0, 0, 0, 1,
};

GLKMatrix4 proj;

GLKVector4 squareVertices[4];

struct vertex
{
	GLKVector4 pos;
	GLuint color;
} data[QUADS_COUNT * VERTS_PER_QUAD];

GLuint vertexBuffer;
GLuint indexBuffer;


void enable_runfast()
{
#ifdef __arm__
    static const unsigned int x = 0x04086060;
    static const unsigned int y = 0x03000000;
    int r;
    asm (
         "fmrx   %0, fpscr                       \n\t"   //r0 = FPSCR
         "and    %0, %0, %1                      \n\t"   //r0 = r0 & 0x04086060
         "orr    %0, %0, %2                      \n\t"   //r0 = r0 | 0x03000000
         "fmxr   fpscr, %0                       \n\t"   //FPSCR = r0
         : "=r"(r)
         : "r"(x), "r"(y)
         );
#endif
}

#define INTRINSICS

#ifdef FPU
#   define float32x4x4_t GLKMatrix4
#   define float32x4_t GLKVector4
#endif


void Matrix4ByVec4(const float32x4x4_t* __restrict__ mat, const float32x4_t* __restrict__ vec, float32x4_t* __restrict__ result);
void Matrix4ByMatrix4(const float32x4x4_t* __restrict__ m1, const float32x4x4_t* __restrict__ m2, float32x4x4_t* __restrict__ r);
void CalculateSpriteVertsWorldPos(const float32x4x4_t* __restrict__ proj, const float32x4x4_t* __restrict__ modelView, float32x4_t* __restrict__ v1, float32x4_t* __restrict__ v2, float32x4_t* __restrict__ v3, float32x4_t* __restrict__ v4);




__attribute__((always_inline)) void Matrix4ByVec4(const float32x4x4_t* __restrict__ mat, const float32x4_t* __restrict__ vec, float32x4_t* __restrict__ result)
{
#ifdef INTRINSICS
    (*result) = vmulq_n_f32((*mat).val[0], (*vec)[0]);
    
    (*result) = vmlaq_n_f32((*result), (*mat).val[1], (*vec)[1]);
    (*result) = vmlaq_n_f32((*result), (*mat).val[2], (*vec)[2]);
    (*result) = vmlaq_n_f32((*result), (*mat).val[3], (*vec)[3]);
#elif defined (ASM)
    asm
    (
     "vldmia %0, { d24-d31 } \n\t"
     "vld1.32    {q1}, [%1]\n\t"
     
     "vmul.f32 q0, q12, d2[0]\n\t"
     
     "vmla.f32 q0, q13, d2[1]\n\t"
     "vmla.f32 q0, q14, d3[0]\n\t"
     "vmla.f32 q0, q15, d3[1]\n\t"
     
     "vstmia %2, { q0 }"
     
     :
     : "r" (mat), "r" (vec), "r" (result)
     : "memory", "q0", "q1", "q8", "q9", "q10", "q11"
     );
#elif defined (GLKIT)
    *(GLKVector4*)result = GLKMatrix4MultiplyVector4(*(GLKMatrix4*)mat, *(GLKVector4*)vec);
#elif defined (FPU)
    GLKMatrix4* m = (GLKMatrix4*)mat;
    GLKVector4* v = (GLKVector4*)vec;
    GLKVector4* r = (GLKVector4*)result;
    r->v[0] = m->m[0] * v->v[0] + m->m[4] * v->v[1] + m->m[8] * v->v[2] + m->m[12] * v->v[3];
    r->v[1] = m->m[1] * v->v[0] + m->m[5] * v->v[1] + m->m[9] * v->v[2] + m->m[13] * v->v[3];
    r->v[2] = m->m[2] * v->v[0] + m->m[6] * v->v[1] + m->m[10] * v->v[2] + m->m[14] * v->v[3];
    r->v[3] = m->m[3] * v->v[0] + m->m[7] * v->v[1] + m->m[11] * v->v[2] + m->m[15] * v->v[3];
#endif
}

__attribute__((always_inline)) void CalculateSpriteVertsWorldPos(const float32x4x4_t* __restrict__ proj, const float32x4x4_t* __restrict__ modelView, float32x4_t* __restrict__ v1, float32x4_t* __restrict__ v2, float32x4_t* __restrict__ v3, float32x4_t* __restrict__ v4)
{
#ifdef ASM
    __asm__ volatile
    (
     "vldmia %6, { q0-q3 } \n\t"
     "vldmia %0, { q8-q11 }\n\t"
     
     "vmul.f32 q12, q8, d0[0]\n\t"
     "vmul.f32 q13, q8, d2[0]\n\t"
     "vmul.f32 q14, q8, d4[0]\n\t"
     "vmul.f32 q15, q8, d6[0]\n\t"
     
     "vmla.f32 q12, q9, d0[1]\n\t"
     "vmla.f32 q13, q9, d2[1]\n\t"
     "vmla.f32 q14, q9, d4[1]\n\t"
     "vmla.f32 q15, q9, d6[1]\n\t"
     
     "vmla.f32 q12, q10, d1[0]\n\t"
     "vmla.f32 q13, q10, d3[0]\n\t"
     "vmla.f32 q14, q10, d5[0]\n\t"
     "vmla.f32 q15, q10, d7[0]\n\t"
     
     "vmla.f32 q12, q11, d1[1]\n\t"
     "vmla.f32 q13, q11, d3[1]\n\t"
     "vmla.f32 q14, q11, d5[1]\n\t"
     "vmla.f32 q15, q11, d7[1]\n\t"
     
     
     "vldmia %1, { q0-q3 } \n\t"
     
     "vmul.f32 q8, q12, d0[0]\n\t"
     "vmul.f32 q9, q12, d2[0]\n\t"
     "vmul.f32 q10, q12, d4[0]\n\t"
     "vmul.f32 q11, q12, d6[0]\n\t"
     
     
     "vmla.f32 q8, q13, d0[1]\n\t"
     "vmla.f32 q8, q14, d1[0]\n\t"
     "vmla.f32 q8, q15, d1[1]\n\t"
     
     
     "vmla.f32 q9, q13, d2[1]\n\t"
     "vmla.f32 q9, q14, d3[0]\n\t"
     "vmla.f32 q9, q15, d3[1]\n\t"
     
     
     "vmla.f32 q10, q13, d4[1]\n\t"
     "vmla.f32 q10, q14, d5[0]\n\t"
     "vmla.f32 q10, q15, d5[1]\n\t"
     
     
     "vmla.f32 q11, q13, d6[1]\n\t"
     "vmla.f32 q11, q14, d7[0]\n\t"
     "vmla.f32 q11, q15, d7[1]\n\t"
     
     "vstmia %2, { q8 }\n\t"
     "vstmia %3, { q9 }\n\t"
     "vstmia %4, { q10 }\n\t"
     "vstmia %5, { q11 }"
     
     :
     : "r" (proj), "r" (squareVertices), "r" (v1), "r" (v2), "r" (v3), "r" (v4), "r" (modelView)
     : "memory", "q0", "q1", "q2", "q3", "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15"
     );
#endif
}


__attribute__((always_inline)) void Matrix4ByMatrix4(const float32x4x4_t* __restrict__ m1, const float32x4x4_t* __restrict__ m2, float32x4x4_t* __restrict__ r)
{
#ifdef INTRINSICS
    (*r).val[0] = vmulq_n_f32((*m1).val[0], vgetq_lane_f32((*m2).val[0], 0));
    (*r).val[1] = vmulq_n_f32((*m1).val[0], vgetq_lane_f32((*m2).val[1], 0));
    (*r).val[2] = vmulq_n_f32((*m1).val[0], vgetq_lane_f32((*m2).val[2], 0));
    (*r).val[3] = vmulq_n_f32((*m1).val[0], vgetq_lane_f32((*m2).val[3], 0));
    
    (*r).val[0] = vmlaq_n_f32((*r).val[0], (*m1).val[1], vgetq_lane_f32((*m2).val[0], 1));
    (*r).val[1] = vmlaq_n_f32((*r).val[1], (*m1).val[1], vgetq_lane_f32((*m2).val[1], 1));
    (*r).val[2] = vmlaq_n_f32((*r).val[2], (*m1).val[1], vgetq_lane_f32((*m2).val[2], 1));
    (*r).val[3] = vmlaq_n_f32((*r).val[3], (*m1).val[1], vgetq_lane_f32((*m2).val[3], 1));
    
    (*r).val[0] = vmlaq_n_f32((*r).val[0], (*m1).val[2], vgetq_lane_f32((*m2).val[0], 2));
    (*r).val[1] = vmlaq_n_f32((*r).val[1], (*m1).val[2], vgetq_lane_f32((*m2).val[1], 2));
    (*r).val[2] = vmlaq_n_f32((*r).val[2], (*m1).val[2], vgetq_lane_f32((*m2).val[2], 2));
    (*r).val[3] = vmlaq_n_f32((*r).val[3], (*m1).val[2], vgetq_lane_f32((*m2).val[3], 2));
    
    (*r).val[0] = vmlaq_n_f32((*r).val[0], (*m1).val[3], vgetq_lane_f32((*m2).val[0], 3));
    (*r).val[1] = vmlaq_n_f32((*r).val[1], (*m1).val[3], vgetq_lane_f32((*m2).val[1], 3));
    (*r).val[2] = vmlaq_n_f32((*r).val[2], (*m1).val[3], vgetq_lane_f32((*m2).val[2], 3));
    (*r).val[3] = vmlaq_n_f32((*r).val[3], (*m1).val[3], vgetq_lane_f32((*m2).val[3], 3));
#elif defined (ASM)
    asm
    (
     "vldmia %1, { q0-q3 } \n\t"
     "vldmia %2, { q8-q11 }\n\t"
     
     "vmul.f32 q12, q8, d0[0]\n\t"
     "vmul.f32 q13, q8, d2[0]\n\t"
     "vmul.f32 q14, q8, d4[0]\n\t"
     "vmul.f32 q15, q8, d6[0]\n\t"
     
     "vmla.f32 q12, q9, d0[1]\n\t"
     "vmla.f32 q13, q9, d2[1]\n\t"
     "vmla.f32 q14, q9, d4[1]\n\t"
     "vmla.f32 q15, q9, d6[1]\n\t"
     
     "vmla.f32 q12, q10, d1[0]\n\t"
     "vmla.f32 q13, q10, d3[0]\n\t"
     "vmla.f32 q14, q10, d5[0]\n\t"
     "vmla.f32 q15, q10, d7[0]\n\t"
     
     "vmla.f32 q12, q11, d1[1]\n\t"
     "vmla.f32 q13, q11, d3[1]\n\t"
     "vmla.f32 q14, q11, d5[1]\n\t"
     "vmla.f32 q15, q11, d7[1]\n\t"
     
     "vstmia %0, { q12-q15 }"
     :
     : "r" (r), "r" (m2), "r" (m1)
     : "memory", "q0", "q1", "q2", "q3", "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15"
     );
#elif defined (GLKIT)
    *(GLKMatrix4*)r = GLKMatrix4Multiply(*(GLKMatrix4*)m1, *(GLKMatrix4*)m2);
#elif defined (FPU)
    GLKMatrix4* l = (GLKMatrix4*)m1;
    GLKMatrix4* m = (GLKMatrix4*)m2;
    GLKMatrix4* h = (GLKMatrix4*)r;
    
    h->m[0] = l->m[0] * m->m[0] + l->m[4] * m->m[1] + l->m[8] * m->m[2] + l->m[12] * m->m[3];
    h->m[1] = l->m[1] * m->m[0] + l->m[5] * m->m[1] + l->m[9] * m->m[2] + l->m[13] * m->m[3];
    h->m[2] = l->m[2] * m->m[0] + l->m[6] * m->m[1] + l->m[10] * m->m[2] + l->m[14] * m->m[3];
    h->m[3] = l->m[3] * m->m[0] + l->m[7] * m->m[1] + l->m[11] * m->m[2] + l->m[15] * m->m[3];
    h->m[4] = l->m[0] * m->m[4] + l->m[4] * m->m[5] + l->m[8] * m->m[6] + l->m[12] * m->m[7];
    h->m[5] = l->m[1] * m->m[4] + l->m[5] * m->m[5] + l->m[9] * m->m[6] + l->m[13] * m->m[7];
    h->m[6] = l->m[2] * m->m[4] + l->m[6] * m->m[5] + l->m[10] * m->m[6] + l->m[14] * m->m[7];
    h->m[7] = l->m[3] * m->m[4] + l->m[7] * m->m[5] + l->m[11] * m->m[6] + l->m[15] * m->m[7];
    h->m[8] = l->m[0] * m->m[8] + l->m[4] * m->m[9] + l->m[8] * m->m[10] + l->m[12] * m->m[11];
    h->m[9] = l->m[1] * m->m[8] + l->m[5] * m->m[9] + l->m[9] * m->m[10] + l->m[13] * m->m[11];
    h->m[10] = l->m[2] * m->m[8] + l->m[6] * m->m[9] + l->m[10] * m->m[10] + l->m[14] * m->m[11];
    h->m[11] = l->m[3] * m->m[8] + l->m[7] * m->m[9] + l->m[11] * m->m[10] + l->m[15] * m->m[11];
    h->m[12] = l->m[0] * m->m[12] + l->m[4] * m->m[13] + l->m[8] * m->m[14] + l->m[12] * m->m[15];
    h->m[13] = l->m[1] * m->m[12] + l->m[5] * m->m[13] + l->m[9] * m->m[14] + l->m[13] * m->m[15];
    h->m[14] = l->m[2] * m->m[12] + l->m[6] * m->m[13] + l->m[10] * m->m[14] + l->m[14] * m->m[15];
    h->m[15] = l->m[3] * m->m[12] + l->m[7] * m->m[13] + l->m[11] * m->m[14] + l->m[15] * m->m[15];
#endif
}

void Update();


void SetupData()
{
	static const GLuint squareColors[] =
	{
		0x00ffffff,
		0xff00ffff,
		0xffff00ff,
		0x00ffffff,
	};
    
    enable_runfast();
    
    squareVertices[0] = GLKVector4Make(20.0f, 20.0f, 0.0f, 1.0f);
    squareVertices[1] = GLKVector4Make(45.0f, 20.0f, 0.0f, 1.0f);
    squareVertices[2] = GLKVector4Make(20.0f, 45.0f, 0.0f, 1.0f);
    squareVertices[3] = GLKVector4Make(45.0f, 45.0f, 0.0f, 1.0f);
    
    
	static const GLushort squareIndeces[] = {0, 1, 2, 1, 3, 2};
	GLushort indexes[QUADS_COUNT * 6];
	GLuint indexDelta = 0;
	for (int i = 0; i < QUADS_COUNT * 6; i += 6)
	{
		indexes[i + 0] = squareIndeces[0] + indexDelta;
		indexes[i + 1] = squareIndeces[1] + indexDelta;
		indexes[i + 2] = squareIndeces[2] + indexDelta;
		indexes[i + 3] = squareIndeces[3] + indexDelta;
		indexes[i + 4] = squareIndeces[4] + indexDelta;
		indexes[i + 5] = squareIndeces[5] + indexDelta;
		indexDelta += VERTS_PER_QUAD;
	}
    
    for (int i = 0; i < QUADS_COUNT * VERTS_PER_QUAD; i += VERTS_PER_QUAD)
	{
        data[i + 0].color = squareColors[0];
        data[i + 1].color = squareColors[1];
        data[i + 2].color = squareColors[2];
        data[i + 3].color = squareColors[3];
    }
    
    
	glGenBuffers(1, &vertexBuffer);
	glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
    
	Update();
    
	glGenBuffers(1, &indexBuffer);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indexes), indexes, GL_STATIC_DRAW);
}

void Update()
{
    GLKMatrix4 modelviewMat =
    {
		1, 0, 0, 0,
		0, 1, 0, 0,
		0, 0, 1, 0,
		0, 0, 0, 1,
	};
    
    
	const float Y_DELTA = 420.0f / QUADS_COUNT;
	float vertDelta = Y_DELTA;
    
    for (int i = 0; i < QUADS_COUNT * VERTS_PER_QUAD; i += VERTS_PER_QUAD)
    {
        float randX = random() % 260;
        
        modelviewMat.m[12] = randX;
        modelviewMat.m[13] = vertDelta;
        
#ifdef ASM
        CalculateSpriteVertsWorldPos((float32x4x4_t*)proj.m, (float32x4x4_t*)modelviewMat.m, (float32x4_t*)&data[i + 0].pos, (float32x4_t*)&data[i + 1].pos, (float32x4_t*)&data[i + 2].pos, (float32x4_t*)&data[i + 3].pos);
#else
        float32x4x4_t modelviewProj;
        Matrix4ByMatrix4((float32x4x4_t*)proj.m, (float32x4x4_t*)modelviewMat.m, &modelviewProj);
        
        for (int j = 0; j < 4; ++j) {
            Matrix4ByVec4(&modelviewProj, (float32x4_t*)&squareVertices[j], (float32x4_t*)&data[i + j].pos);
        }
#endif
        
        vertDelta += Y_DELTA;
    }
	
    glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STREAM_DRAW);
}

void CalcProj(float proj[16])
{
    float right = 320;
	float left = 0;
	float top = 480;
	float bottom = 0;
	float nearPlane = -1;
	float farPlane = 1;
	
	float a = 2.0f / (right - left);
	float b = 2.0f / (top - bottom);
	float c = -2.0f / (farPlane - nearPlane);
    
	float tx = -(right + left) / (right - left);
	float ty = -(top + bottom) / (top - bottom);
	float tz = (farPlane + nearPlane) / (nearPlane - farPlane);
    
	float projection[16] =
	{
		a, 0, 0, 0,
		0, b, 0, 0,
		0, 0, c, 0,
		tx, ty, tz, 1
	};
    
    memcpy(proj, projection, sizeof(projection));
}



@interface ViewController () {
    GLuint _program;
}
@property (strong, nonatomic) EAGLContext *context;

- (void)setupGL;
- (void)tearDownGL;

- (BOOL)loadShaders;
- (BOOL)compileShader:(GLuint *)shader type:(GLenum)type file:(NSString *)file;
- (BOOL)linkProgram:(GLuint)prog;
- (BOOL)validateProgram:(GLuint)prog;
@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
    
    if (!self.context) {
        NSLog(@"Failed to create ES context");
    }
    
    GLKView *view = (GLKView *)self.view;
    view.context = self.context;
    view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
    
    [self setupGL];
}

- (void)viewDidUnload
{
    [super viewDidUnload];
    
    [self tearDownGL];
    
    if ([EAGLContext currentContext] == self.context) {
        [EAGLContext setCurrentContext:nil];
    }
	self.context = nil;
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Release any cached data, images, etc. that aren't in use.
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
        return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
    } else {
        return YES;
    }
}

- (void)setupGL
{
    [EAGLContext setCurrentContext:self.context];
    
    [self loadShaders];
    
    glEnable(GL_DEPTH_TEST);
    
    CalcProj(proj.m);
    
    SetupData();
}

- (void)tearDownGL
{
    [EAGLContext setCurrentContext:self.context];
    
    if (_program) {
        glDeleteProgram(_program);
        _program = 0;
    }
}

#pragma mark - GLKView and GLKViewController delegate methods

- (void)update
{
    Update();
}

- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
    glClearColor(0.65f, 0.65f, 0.65f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    glUseProgram(_program);
    
    glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
    
    const GLuint STRIDE = sizeof(data[0]);
	
	glVertexAttribPointer(ATTRIB_POSITION, 2, GL_FLOAT, 0, STRIDE, NULL);
	glEnableVertexAttribArray(ATTRIB_POSITION);
	glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, 1, STRIDE, (GLvoid*)offsetof(struct vertex, color));
	glEnableVertexAttribArray(ATTRIB_COLOR);
	
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
	glDrawElements(GL_TRIANGLES, QUADS_COUNT * 6, GL_UNSIGNED_SHORT, 0);
    
    // [view presentFramebuffer];
}

#pragma mark -  OpenGL ES 2 shader compilation

- (BOOL)loadShaders
{
    GLuint vertShader, fragShader;
    NSString *vertShaderPathname, *fragShaderPathname;
    
    // Create shader program.
    _program = glCreateProgram();
    
    // Create and compile vertex shader.
    vertShaderPathname = [[NSBundle mainBundle] pathForResource:@"Shader" ofType:@"vsh"];
    if (![self compileShader:&vertShader type:GL_VERTEX_SHADER file:vertShaderPathname]) {
        NSLog(@"Failed to compile vertex shader");
        return NO;
    }
    
    // Create and compile fragment shader.
    fragShaderPathname = [[NSBundle mainBundle] pathForResource:@"Shader" ofType:@"fsh"];
    if (![self compileShader:&fragShader type:GL_FRAGMENT_SHADER file:fragShaderPathname]) {
        NSLog(@"Failed to compile fragment shader");
        return NO;
    }
    
    // Attach vertex shader to program.
    glAttachShader(_program, vertShader);
    
    // Attach fragment shader to program.
    glAttachShader(_program, fragShader);
    
    // Bind attribute locations.
    // This needs to be done prior to linking.
    glBindAttribLocation(_program, ATTRIB_POSITION, "position");
    glBindAttribLocation(_program, ATTRIB_COLOR, "sourceColor");
    
    // Link program.
    if (![self linkProgram:_program]) {
        NSLog(@"Failed to link program: %d", _program);
        
        if (vertShader) {
            glDeleteShader(vertShader);
            vertShader = 0;
        }
        if (fragShader) {
            glDeleteShader(fragShader);
            fragShader = 0;
        }
        if (_program) {
            glDeleteProgram(_program);
            _program = 0;
        }
        
        return NO;
    }
    
    // Release vertex and fragment shaders.
    if (vertShader) {
        glDetachShader(_program, vertShader);
        glDeleteShader(vertShader);
    }
    if (fragShader) {
        glDetachShader(_program, fragShader);
        glDeleteShader(fragShader);
    }
    
    return YES;
}

- (BOOL)compileShader:(GLuint *)shader type:(GLenum)type file:(NSString *)file
{
    GLint status;
    const GLchar *source;
    
    source = (GLchar *)[[NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:nil] UTF8String];
    if (!source) {
        NSLog(@"Failed to load vertex shader");
        return NO;
    }
    
    *shader = glCreateShader(type);
    glShaderSource(*shader, 1, &source, NULL);
    glCompileShader(*shader);
    
#if defined(DEBUG)
    GLint logLength;
    glGetShaderiv(*shader, GL_INFO_LOG_LENGTH, &logLength);
    if (logLength > 0) {
        GLchar *log = (GLchar *)malloc(logLength);
        glGetShaderInfoLog(*shader, logLength, &logLength, log);
        NSLog(@"Shader compile log:\n%s", log);
        free(log);
    }
#endif
    
    glGetShaderiv(*shader, GL_COMPILE_STATUS, &status);
    if (status == 0) {
        glDeleteShader(*shader);
        return NO;
    }
    
    return YES;
}

- (BOOL)linkProgram:(GLuint)prog
{
    GLint status;
    glLinkProgram(prog);
    
#if defined(DEBUG)
    GLint logLength;
    glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logLength);
    if (logLength > 0) {
        GLchar *log = (GLchar *)malloc(logLength);
        glGetProgramInfoLog(prog, logLength, &logLength, log);
        NSLog(@"Program link log:\n%s", log);
        free(log);
    }
#endif
    
    glGetProgramiv(prog, GL_LINK_STATUS, &status);
    if (status == 0) {
        return NO;
    }
    
    return YES;
}

- (BOOL)validateProgram:(GLuint)prog
{
    GLint logLength, status;
    
    glValidateProgram(prog);
    glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logLength);
    if (logLength > 0) {
        GLchar *log = (GLchar *)malloc(logLength);
        glGetProgramInfoLog(prog, logLength, &logLength, log);
        NSLog(@"Program validate log:\n%s", log);
        free(log);
    }
    
    glGetProgramiv(prog, GL_VALIDATE_STATUS, &status);
    if (status == 0) {
        return NO;
    }
    
    return YES;
}

@end
